package org.biz.common;


import org.apache.poi.xwpf.usermodel.*;
import org.springframework.core.io.ClassPathResource;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * @Description: 通过word模板生成新的word工具类
 * @Author: jeecg-boot
 * @Date: 2022-03-23
 * @Version: V1.0
 */
public class WordUtils {

    /**
     * 根据模板生成word
     *
     * @param params    需要替换的参数
     * @param tableList 需要插入的参数
     * @param fileName  word文件的文件名
     * @param response
     */
    public void getWord(Map<String, Object> params, List<String[]> tableList, String fileName, HttpServletResponse response) throws Exception {

//        InputStream is = new FileInputStream(file);
        //使用流读取相对路径模板
        InputStream is = new ClassPathResource("/exportTemplate/" + fileName).getInputStream();
        CustomXWPFDocument doc = new CustomXWPFDocument(is);
        this.replaceInPara(doc, params);    //替换文本里面的变量
//        this.replaceInTable(doc, params, tableList); //替换表格里面的变量
//        OutputStream os = response.getOutputStream();
//        response.setHeader("Content-disposition", "attachment; filename=" + fileName);
        fileName = CommonUtils.getFileName(fileName);
        String objectName = fileName.substring(0, fileName.lastIndexOf(".")) + "_" + System.currentTimeMillis() + fileName.substring(fileName.lastIndexOf("."));
        File newFile = new File(objectName);
        FileOutputStream os = new FileOutputStream(newFile);
        doc.write(os);
        FileInputStream fileInput = new FileInputStream(newFile);

        doc.close();
        this.close(os);
        this.close(is);
        this.close(fileInput);
        boolean delete = newFile.delete();
//        return fileUrl;
    }

    /**
     * 根据模板生成word
     *
     * @param params    需要替换的参数
     * @param tableList 需要插入的参数
     * @param fileName  生成word文件的文件名
     * @param response
     */
    public void getWord2(Map<String, Object> params, List<String> tableList, String fileName, HttpServletResponse response) throws Exception {
        //使用流读取相对路径模板
        try (InputStream is = new ClassPathResource("/templates/" + fileName).getInputStream();
             OutputStream os = response.getOutputStream()) {
            CustomXWPFDocument doc = new CustomXWPFDocument(is);
            this.replaceInPara(doc, params);
            response.setContentType("application/x-download");
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-disposition", "attachment; filename=" +
                    new String(fileName.getBytes(), StandardCharsets.ISO_8859_1));
            doc.write(os);
        } catch (Exception e) {
            System.out.println("导出 Word 失败"+e);
            e.printStackTrace();
            throw e;
        }

    }

    /**
     * 替换段落里面的变量
     *
     * @param doc    要替换的文档
     * @param params 参数
     */
    private void replaceInPara(CustomXWPFDocument doc, Map<String, Object> params) throws Exception{


        Iterator<XWPFParagraph> iterator = doc.getParagraphsIterator();
        XWPFParagraph para;
        while (iterator.hasNext()) {
            para = iterator.next();
//            this.replaceInPara(para, params, doc);
            this.replaceInPara2(para, params, doc);
        }
    }

    /**
     * 替换段落里面的变量
     *
     * @param para   要替换的段落
     * @param params 参数
     */
    private void replaceInPara(XWPFParagraph para, Map<String, Object> params, CustomXWPFDocument doc) {

        List<XWPFRun> runs;
        Matcher matcher;
//        para.setAlignment(ParagraphAlignment.LEFT);//对齐方式
        if (this.matcher(para.getParagraphText()).find()) {
            runs = para.getRuns();
            int start = -1;
            int end = -1;
            String str = "";
            for (int i = 0; i < runs.size(); i++) {
                XWPFRun run = runs.get(i);
                String runText = run.toString();

                if ('$' == runText.charAt(0) && '[' == runText.charAt(1)) {
                    start = i;
                }
                if ((start != -1)) {
                    str += runText;
                }
                if (']' == runText.charAt(runText.length() - 1)) {
                    if (start != -1) {
                        end = i;
                        replaceString(para, params, doc, str);
                        str = "";
                    }
                }
            }

            for (int i = start; i <= end; i++) {
                para.removeRun(i);
                i--;
                end--;
            }
        }
    }

    private void replaceString(XWPFParagraph para, Map<String, Object> params, CustomXWPFDocument doc, String str) {
        for (Map.Entry<String, Object> entry : params.entrySet()) {
            String key = entry.getKey();
            if (str.indexOf(key) != -1) {
                Object value = entry.getValue();
                if (value instanceof String) {
                    str = str.replace(key, value.toString());
                    XWPFRun paraRun = para.createRun();
                    paraRun.setFontFamily("黑体");
                    paraRun.setFontSize(12);
                    paraRun.setText(str, 0);
//                        para.createRun().setText(str, 0);
//                        para.createRun().setFontFamily("黑体");
//                        para.createRun().setFontSize(12);
                    break;
                } else if (value instanceof Map) {
                    str = str.replace(key, "");
                    Map pic = (Map) value;
                    int width = Integer.parseInt(pic.get("width").toString());
                    int height = Integer.parseInt(pic.get("height").toString());
                    int picType = getPictureType(pic.get("type").toString());
                    byte[] byteArray = (byte[]) pic.get("content");
                    ByteArrayInputStream byteInputStream = new ByteArrayInputStream(byteArray);
                    try {
                        //int ind = doc.addPicture(byteInputStream,picType);
                        //doc.createPicture(ind, width , height,para);
                        //添加图片
                        doc.addPictureData(byteInputStream, picType);
                        //图片大小、位置
                        doc.createPicture(doc.getAllPictures().size() - 1, width, height, para);
                        XWPFRun paraRun = para.createRun();
                        paraRun.setText(str, 0);
                        paraRun.setFontFamily("黑体");
                        paraRun.setFontSize(12);
                        break;
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                } else if (value instanceof List) {
                    List<String> pic = (List<String>) value;
                    for (String s : pic) {
                        XWPFRun paraRun = para.createRun();
                        paraRun.setFontFamily("黑体");
                        paraRun.setFontSize(12);
                        paraRun.setText(s, 0);
                        paraRun.addCarriageReturn();
                    }
                }
            }
        }
    }


    /**
     * 为表格插入数据，行数不够添加新行
     *
     * @param table     需要插入数据的表格
     * @param tableList 插入数据集合
     */
    private static void insertTable(XWPFTable table, List<String> tableList) {
        //创建行,根据需要插入的数据添加新行，不处理表头
        for (int i = 0; i < tableList.size(); i++) {
            XWPFTableRow row = table.createRow();
        }
        //遍历表格插入数据
        List<XWPFTableRow> rows = table.getRows();
        int length = table.getRows().size();
        for (int i = 1; i < length - 1; i++) {
            XWPFTableRow newRow = table.getRow(i);
            List<XWPFTableCell> cells = newRow.getTableCells();
            for (int j = 0; j < cells.size(); j++) {
                XWPFTableCell cell = cells.get(j);
                String s = tableList.get(i - 1);
                cell.setText(s);
            }
        }
    }

    /**
     * 替换表格里面的变量
     *
     * @param doc    要替换的文档
     * @param params 参数
     */
    private void replaceInTable(CustomXWPFDocument doc, Map<String, Object> params, List<String> tableList) {
        Iterator<XWPFTable> iterator = doc.getTablesIterator();
        XWPFTable table;
        List<XWPFTableRow> rows;
        List<XWPFTableCell> cells;
        List<XWPFParagraph> paras;
        while (iterator.hasNext()) {
            table = iterator.next();
            if (table.getRows().size() > 1) {
                //判断表格是需要替换还是需要插入，判断逻辑有$为替换，表格无$为插入
                if (this.matcher(table.getText()).find()) {
                    rows = table.getRows();
                    for (XWPFTableRow row : rows) {
                        cells = row.getTableCells();
                        for (XWPFTableCell cell : cells) {
                            paras = cell.getParagraphs();
                            for (XWPFParagraph para : paras) {
                                this.replaceInPara(para, params, doc);
                            }
                        }
                    }
                } else {
                    insertTable(table, tableList);  //插入数据
                }
            }
        }
    }


    /**
     * 正则匹配字符串
     *
     * @param str
     * @return
     */
    private Matcher matcher(String str) {
        Pattern pattern = Pattern.compile("\\$\\{(.+?)\\}", Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(str);
        return matcher;
    }


    /**
     * 根据图片类型，取得对应的图片类型代码
     *
     * @param picType
     * @return int
     */
    private static int getPictureType(String picType) {
        int res = CustomXWPFDocument.PICTURE_TYPE_PICT;
        if (picType != null) {
            if (picType.equalsIgnoreCase("png")) {
                res = CustomXWPFDocument.PICTURE_TYPE_PNG;
            } else if (picType.equalsIgnoreCase("dib")) {
                res = CustomXWPFDocument.PICTURE_TYPE_DIB;
            } else if (picType.equalsIgnoreCase("emf")) {
                res = CustomXWPFDocument.PICTURE_TYPE_EMF;
            } else if (picType.equalsIgnoreCase("jpg") || picType.equalsIgnoreCase("jpeg")) {
                res = CustomXWPFDocument.PICTURE_TYPE_JPEG;
            } else if (picType.equalsIgnoreCase("wmf")) {
                res = CustomXWPFDocument.PICTURE_TYPE_WMF;
            }
        }
        return res;
    }

    /**
     * 将输入流中的数据写入字节数组
     *
     * @param in
     * @return
     */
    public static byte[] inputStream2ByteArray(InputStream in, boolean isClose) {
        byte[] byteArray = null;
        try {
            int total = in.available();
            byteArray = new byte[total];
            in.read(byteArray);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (isClose) {
                try {
                    in.close();
                } catch (Exception e2) {
                    e2.getStackTrace();
                }
            }
        }
        return byteArray;
    }


    /**
     * 关闭输入流
     *
     * @param is
     */
    private void close(InputStream is) {
        if (is != null) {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 关闭输出流
     *
     * @param os
     */
    private void close(OutputStream os) {
        if (os != null) {
            try {
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void replaceInPara3(XWPFParagraph para, Map<String, Object> params, CustomXWPFDocument doc) {
        List<XWPFRun> runs;
        Matcher matcher;
        String runText = "";
        int fontSize = 0;
        UnderlinePatterns underlinePatterns = null;
        if (this.matcher(para.getParagraphText()).find()) {
            runs = para.getRuns();
            if (runs.size() > 0) {
                int j = runs.size();
                for (int i = 0; i < j; i++) {
                    XWPFRun run = runs.get(0);
                    if (fontSize == 0) {
                        fontSize = run.getFontSize();
                    }
                    if (underlinePatterns == null) {
                        underlinePatterns = run.getUnderline();
                    }
                    String i1 = run.toString();
                    runText += i1;
                    para.removeRun(0);
                }
            }
            boolean flag = true;
            matcher = this.matcher(runText);
            if (matcher.find()) {
                while ((matcher = this.matcher(runText)).find()) {
                    if (params.get(matcher.group(1)) instanceof String) {
                        String value = String.valueOf(params.get(matcher.group(1)));
                        runText = matcher.replaceFirst(value);
                    } else if ((params.get(matcher.group(1)) instanceof List)) {
                        List<String> pic = (List<String>) params.get(matcher.group(1));
                        for (String s : pic) {
                            XWPFRun run = para.createRun();
                            run.setFontSize(fontSize);
                            run.setUnderline(underlinePatterns);
//                            run.setText(s, 0);
                            run.addCarriageReturn();
                            if (s.contains("$$")) {
                                run.setText(s.substring(0, s.indexOf("$$")));
                                run = para.createRun();
                                run.setText("101");
                                run.setColor("FF0000"); // 设置红色
                                run.setFontSize(fontSize);
                                run = para.createRun();
                                run.setText(s.substring(s.indexOf("$$")+2));
                                run.setFontSize(fontSize);
                            }
                        }
                        flag = false;
                        break;
                    } else if ((params.get(matcher.group(1)) instanceof Map)) {
                        Map pic = (Map) params.get(matcher.group(1));
                        int width = Integer.parseInt(pic.get("width").toString());
                        int height = Integer.parseInt(pic.get("height").toString());
                        int picType = getPictureType(pic.get("type").toString());
                        byte[] byteArray = (byte[]) pic.get("content");
                        ByteArrayInputStream byteInputStream = new ByteArrayInputStream(byteArray);
                        try {
                            //int ind = doc.addPicture(byteInputStream,picType);
                            //doc.createPicture(ind, width , height,para);
                            //添加图片
                            doc.addPictureData(byteInputStream, picType);
                            //图片大小、位置
                            doc.createPicture(doc.getAllPictures().size() - 1, width, height, para);
//                            XWPFRun paraRun = para.createRun();
//                            paraRun.setText(runText, 0);
//                            paraRun.setFontFamily("黑体");
//                            paraRun.setFontSize(12);
                            break;
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
                //直接调用XWPFRun的setText()方法设置文本时，在底层会重新创建一个XWPFRun，把文本附加在当前文本后面，
                //所以我们不能直接设值，需要先删除当前run,然后再自己手动插入一个新的run。
//                para.insertNewRun(0).setText(runText);//新增的没有样式
                if (flag) {
                    XWPFRun run = para.createRun();
//                run.setBold(true); //加粗
                    run.setText(runText, 0);
                    run.setFontSize(fontSize);
                    run.setUnderline(underlinePatterns);
//                run.setColor("FF0000");
                }
            }
        }
    }

    /**
     * 替换段落里面的变量
     *
     * @param para   要替换的段落
     * @param params 参数
     */
    private void replaceInPara2(XWPFParagraph para, Map<String, Object> params, CustomXWPFDocument doc) {
        List<XWPFRun> runs;
        Matcher matcher;
        String runText = "";
        int fontSize = 0;
//        System.out.println("匹配到的文本: " + para.getParagraphText());
        UnderlinePatterns underlinePatterns = null;
        for (XWPFTable table : doc.getTables()) {
            for (XWPFTableRow row : table.getRows()) {
                for (XWPFTableCell cell : row.getTableCells()) {
                    String cellText = cell.getText();
                    if (this.matcher(cellText).find()) { // 发现占位符
                        for (Map.Entry<String, Object> entry : params.entrySet()) {
                            cellText = cellText.replace("${" + entry.getKey() + "}", String.valueOf(entry.getValue()));
                        }
                        cell.removeParagraph(0);
                        cell.setText(cellText);
                    }
                }
            }
        }
        if (this.matcher(para.getParagraphText()).find()) {
            runs = para.getRuns();
            if (runs.size() > 0) {
                int j = runs.size();
                for (int i = 0; i < j; i++) {
                    XWPFRun run = runs.get(0);
                    if (fontSize == 0) {
                        fontSize = run.getFontSize();
                    }
                    if (underlinePatterns == null) {
                        underlinePatterns = run.getUnderline();
                    }
                    String i1 = run.toString();
                    runText += i1;
                    para.removeRun(0);
                }
            }
//            System.out.println("解析到的文本：" + runText); // 输出完整的runText
            boolean flag = true;
            matcher = this.matcher(runText);
            if (matcher.find()) {
                while ((matcher = this.matcher(runText)).find()) {
                    String key = matcher.group(1);
                    Object object = params.get(key);
                    if (object == null) {
                        System.out.println("未找到匹配项：" + key);
                        continue; // 跳过未找到的值
                    }
//                    System.out.println("匹配到变量：" + key + "，值：" + params.get(key));
                    if (params.get(matcher.group(1)) instanceof String) {
                        String value = String.valueOf(params.get(matcher.group(1)));
                        runText = matcher.replaceFirst(value);
                    } else if ((params.get(matcher.group(1)) instanceof Integer)) {
                        String value = String.valueOf(params.get(matcher.group(1))); // 转换 Integer 为 String
                        runText = matcher.replaceFirst(value);
                    }
                    else if ((params.get(matcher.group(1)) instanceof List)) {
                        List<String> pic = (List<String>) params.get(matcher.group(1));
                        for (String s : pic) {
                            XWPFRun run = para.createRun();
                            run.setFontSize(fontSize);
                            run.setUnderline(underlinePatterns);
                            run.setText(s, 0);
                            run.addCarriageReturn();
                        }
                        flag = false;
                        break;
                    } else if ((params.get(matcher.group(1)) instanceof Map)) {
                        Map pic = (Map) params.get(matcher.group(1));
                        int width = Integer.parseInt(pic.get("width").toString());
                        int height = Integer.parseInt(pic.get("height").toString());
                        int picType = getPictureType(pic.get("type").toString());
                        byte[] byteArray = (byte[]) pic.get("content");
                        ByteArrayInputStream byteInputStream = new ByteArrayInputStream(byteArray);
                        try {
                            //添加图片
                            doc.addPictureData(byteInputStream, picType);
                            //图片大小、位置
                            doc.createPicture(doc.getAllPictures().size() - 1, width, height, para);
                            break;
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
                if (flag) {
                    XWPFRun run = para.createRun();
//                run.setBold(true); //加粗
                    run.setText(runText, 0);
                    run.setFontSize(fontSize);
                    run.setUnderline(underlinePatterns);
                }
            }
        }

    }

    /**
     * 替换段落里面的变量
     *
     * @param para   要替换的段落
     * @param params 参数
     */
    private void replaceInPara3(XWPFParagraph para, Map<String, Object> params) {
        List<XWPFRun> runs;
        Matcher matcher;
        if (this.matcher(para.getParagraphText()).find()) {
            runs = para.getRuns();
            for (int i = 0; i < runs.size(); i++) {
                XWPFRun run = runs.get(i);
                String runText = run.toString();
                matcher = this.matcher(runText);
                if (matcher.find()) {
                    while ((matcher = this.matcher(runText)).find()) {
                        Object o = params.get(matcher.group(1));
                        runText = matcher.replaceFirst(String.valueOf(params.get(matcher.group(1))));
                    }
                    //直接调用XWPFRun的setText()方法设置文本时，在底层会重新创建一个XWPFRun，把文本附加在当前文本后面，
                    //所以我们不能直接设值，需要先删除当前run,然后再自己手动插入一个新的run。
                    para.removeRun(i);
                    para.insertNewRun(i).setText(runText);
                }
            }
        }
    }

}
